package com.example.testapp.activity.view.pager;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Camera;
import android.graphics.Color;
import android.graphics.Matrix;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import com.example.testapp.R;
import com.example.testapp.activity.ui.OutlineContainer;
import com.nineoldandroids.view.ViewHelper;

import java.util.HashMap;
import java.util.LinkedHashMap;

/**
 * Created by l on 2016/9/5.
 */
public class TestViewPager extends ViewPager {
    public static final String TAG = "TestViewPager";

    private boolean mEnabled = true;
    private boolean mFadeEnabled = false;
    private boolean mOutlineEnabled = false;
    public static int sOutlineColor = Color.WHITE;
    private TransitionEffect mEffect = TransitionEffect.Standard;

    private HashMap<Integer, Object> mObjs = new LinkedHashMap<Integer, Object>();

    private static final float SCALE_MAX = 0.5f;
    private static final float ZOOM_MAX = 0.5f;
    private static final float ROT_MAX = 15.0f;

    public enum TransitionEffect{
        Standard, Tablet, CubeIn, CubeOut, FlipVertical, FlipHorizontal, Stack, ZoomIn, ZoomOut, RotateUp, RotateDown, Accordion,
    }

    public TestViewPager(Context context) {
        this(context, null);
    }

    public TestViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        setClipChildren(false);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.JazzyViewPager);
        int effect = ta.getInt(R.styleable.JazzyViewPager_style, 0);
        String[] transitions = getResources().getStringArray(R.array.jazzy_effects);
        setTransitionEffect(TransitionEffect.valueOf(transitions[effect]));
        setFadeEnabled(ta.getBoolean(R.styleable.JazzyViewPager_fadeEnabled, false));
        setOutlineEnabled(ta.getBoolean(R.styleable.JazzyViewPager_outlineEnabled, false));
        setOutlineColor(ta.getColor(R.styleable.JazzyViewPager_outlineColor, Color.WHITE));
        switch (mEffect){
            case Stack:
            case ZoomOut:
                setFadeEnabled(true);
        }
        ta.recycle();
    }

    public void setTransitionEffect(TransitionEffect mEffect) {
        this.mEffect = mEffect;
    }

    public void setPagingEnabled(boolean enabled) {
        this.mEnabled = enabled;
    }

    public void setFadeEnabled(boolean fadeEnabled) {
        this.mFadeEnabled = fadeEnabled;
    }

    public void setOutlineEnabled(boolean outlineEnabled) {
        this.mOutlineEnabled = outlineEnabled;
        wrapWithOutlines();
    }

    public static void setOutlineColor(int outlineColor) {
        TestViewPager.sOutlineColor = outlineColor;
    }

    private void wrapWithOutlines() {
         for(int i = 0; i < getChildCount(); i++){
             View v = getChildAt(i);
             if(!(v instanceof OutlineContainer)){
                 removeView(v);
                 super.addView(wrapChild(v), i);
             }
         }
    }

    private View wrapChild(View child) {
        if(!mOutlineEnabled || child instanceof OutlineContainer)
            return child;
        OutlineContainer out = new OutlineContainer(getContext());
        out.setLayoutParams(generateDefaultLayoutParams());
        child.setLayoutParams(new OutlineContainer.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        out.addView(child);
        return out;
    }

    @Override
    public void addView(View child) {
        super.addView(wrapChild(child));
    }

    @Override
    public void addView(View child, int index) {
        super.addView(wrapChild(child), index);
    }

    @Override
    public void addView(View child, ViewGroup.LayoutParams params) {
        super.addView(wrapChild(child), params);
    }

    @Override
    public void addView(View child, int width, int height) {
        super.addView(wrapChild(child), width, height);
    }

    public void addView(View child, int index, LayoutParams params) {
        super.addView(wrapChild(child), index, params);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mEnabled ? super.onInterceptTouchEvent(ev) : false;
    }

    private State mState;
    private int oldPage;

    private View mLeft;
    private View mRight;
    private float mRot;
    private float mTrans;
    private float mScale;

    private enum State{
        IDLE, GOING_LEFT, GOING_RIGHT
    }

    private void logState(View v, String title) {
        Log.v(TAG, title + ": ROT (" + ViewHelper.getRotation(v) + ", "
                + ViewHelper.getRotationX(v) + ", "
                + ViewHelper.getRotationY(v) + "), TRANS ("
                + ViewHelper.getTranslationX(v) + ", "
                + ViewHelper.getTranslationY(v) + "), SCALE ("
                + ViewHelper.getScaleX(v) + ", "
                + ViewHelper.getScaleY(v) + "), ALPHA "
                + ViewHelper.getAlpha(v));
    }

    private void animateTablet(View left, View right, float positionOffset) {
        if(mState != State.IDLE){
            if(left != null){
                mRot = 30.0f * positionOffset;
                mTrans = getOffsetXForRotation(mRot, left.getMeasuredWidth(), left.getMeasuredHeight());
                ViewHelper.setPivotX(left, left.getMeasuredWidth() / 2);
                ViewHelper.setPivotY(left, left.getMeasuredHeight() / 2);
                ViewHelper.setTranslationX(left, mTrans);
                ViewHelper.setRotationY(left, mRot);
                logState(left, "Left");
            }
            if(right != null){
                mRot = -30.0f * (1 - positionOffset);
                mTrans = getOffsetXForRotation(mRot, right.getMeasuredWidth(), right.getMeasuredHeight());
                ViewHelper.setPivotX(right, right.getMeasuredWidth() / 0.5f);
                ViewHelper.setPivotY(right, right.getMeasuredHeight() / 0.5f);
                ViewHelper.setTranslationX(right, mTrans);
                ViewHelper.setRotationY(right, mRot);
                logState(right, "Right");
            }
        }
    }

    private void animateCube(View left, View right, float effectOffset, boolean in) {
         if(mState != State.IDLE){
             if(left != null){
                 mRot = (in ? 90.0f : -90.0f) * effectOffset;
                 ViewHelper.setPivotX(left, left.getMeasuredWidth());
                 ViewHelper.setPivotY(left, left.getMeasuredHeight() * 0.5f);
                 ViewHelper.setRotationY(left, mRot);
             }
             if(right != null){
                 mRot = -(in ? 90.0f : -90.0f) * (1 - effectOffset);
                 ViewHelper.setPivotX(right, 0);
                 ViewHelper.setPivotY(right, right.getMeasuredHeight() * 0.5f);
                 ViewHelper.setRotationY(right, mRot);
             }
         }
    }

    private void animateFlipHorizontal(View left, View right, float offset, int positionOffsetPixels) {
        if(mState != State.IDLE){
            if(left != null){
                mRot = 180.0f * offset;
                if(mRot > 90.0f){
                    left.setVisibility(View.INVISIBLE);
                }else {
                    if(left.getVisibility() == View.INVISIBLE)
                        left.setVisibility(VISIBLE);
                    mTrans = positionOffsetPixels;
                    ViewHelper.setPivotX(left, left.getMeasuredWidth() * 0.5f);
                    ViewHelper.setPivotY(left, left.getMeasuredHeight() * 0.5f);
                    ViewHelper.setTranslationX(left, mTrans);
                    ViewHelper.setRotationY(left, mRot);
                }
            }
            if(right != null){
                mRot = -180.0f * (1-offset);
                if(mRot < -90.0f){
                    right.setVisibility(View.INVISIBLE);
                }else {
                    if(right.getVisibility() == View.INVISIBLE)
                        right.setVisibility(VISIBLE);
                    mTrans = positionOffsetPixels;
                    ViewHelper.setPivotX(right, right.getMeasuredWidth() * 0.5f);
                    ViewHelper.setPivotY(right, right.getMeasuredHeight() * 0.5f);
                    ViewHelper.setTranslationX(right, mTrans);
                    ViewHelper.setRotationY(right, mRot);
                }
            }
        }
    }

    private void animateFlipVertical(View left, View right, float offset, int positionOffsetPixels) {
          if(mState != State.IDLE){
              if(left != null){
                  mRot = 180.0f * offset;
                  if(mRot > 90.0f){
                      left.setVisibility(View.INVISIBLE);
                  }else{
                      if(left.getVisibility() == View.INVISIBLE)
                          left.setVisibility(VISIBLE);
                      mTrans = positionOffsetPixels;
                      ViewHelper.setPivotX(left, left.getMeasuredWidth() * 0.5f);
                      ViewHelper.setPivotY(left, left.getMeasuredHeight() * 0.5f);
                      ViewHelper.setTranslationX(left, mTrans);
                      ViewHelper.setRotationX(left, mRot);
                  }
              }
              if(right != null){
                  mRot = -180.0f * (1 - offset);
                  if(mRot < -90.0f){
                      right.setVisibility(View.INVISIBLE);
                  }else{
                      if(right.getVisibility() == View.VISIBLE)
                          right.setVisibility(VISIBLE);
                      mTrans = -getWidth() - getPageMargin() + positionOffsetPixels;
                      ViewHelper.setPivotX(right, right.getMeasuredWidth() * 0.5f);
                      ViewHelper.setPivotY(right, right.getMeasuredHeight() * 0.5f);
                      ViewHelper.setTranslationX(right, mTrans);
                      ViewHelper.setRotationX(right, mRot);
                  }
              }
          }
    }

    private void animateRotate(View left, View right, float offset, boolean up) {
          if(mState != State.IDLE){
              if(left != null){
                 mRot = (up ? 1 : -1)*(ROT_MAX * offset);
                 mTrans = (up ? -1 : 1)*(float)(getMeasuredHeight() - getMeasuredHeight() * Math.cos(mRot * Math.PI / 180.0f));
                  ViewHelper.setPivotX(left, left.getMeasuredWidth() * 0.5f);
                  ViewHelper.setPivotY(left, up ? 0 :left.getMeasuredHeight() * 0.5f);
                  ViewHelper.setTranslationY(left,  mTrans);
                  ViewHelper.setRotation(left, mRot);
              }
              if(right != null){
                  mRot = (up ? 1 : -1)*(-ROT_MAX + ROT_MAX * offset);
                  mTrans = (up ? -1 : 1)*(float)(getMeasuredHeight() - getMeasuredHeight() * Math.cos(mRot * Math.PI / 180.0f));
                  ViewHelper.setPivotX(right, right.getMeasuredWidth() * 0.5f);
                  ViewHelper.setPivotY(right, up ? 0 :right.getMeasuredHeight() * 0.5f);
                  ViewHelper.setTranslationY(right,  mTrans);
                  ViewHelper.setRotation(right, mRot);
              }
          }
    }

    private void animateAccordion(View left, View right, float offset) {
         if(mState != State.IDLE){
             if (left != null){
                ViewHelper.setPivotX(left, left.getMeasuredWidth());
                ViewHelper.setPivotY(left, 0);
                ViewHelper.setScaleX(left, 1 - offset);
             }
             if(right != null){
                 ViewHelper.setPivotX(right, 0);
                 ViewHelper.setPivotY(right, 0);
                 ViewHelper.setScaleX(right, offset);
             }
         }
    }

    private void animateZoom(View left, View right, float offset, boolean in) {
         if(mState != State.IDLE){
             if(left != null){
                 mScale = in ? ZOOM_MAX + (1 - ZOOM_MAX) * (1 - offset) : 1 + ZOOM_MAX - ZOOM_MAX * (1 - offset);
                 ViewHelper.setPivotX(left, left.getMeasuredWidth() * 0.5f);
                 ViewHelper.setPivotY(left, left.getMeasuredHeight() * 0.5f);
                 ViewHelper.setScaleX(left, mScale);
                 ViewHelper.setScaleY(left, mScale);
             }
             if(right != null){
                 mScale = in ? ZOOM_MAX + (1 - ZOOM_MAX) * offset : 1 + ZOOM_MAX - ZOOM_MAX * offset;
                 ViewHelper.setPivotX(right, right.getMeasuredWidth() * 0.5f);
                 ViewHelper.setPivotY(right, right.getMeasuredHeight() * 0.5f);
                 ViewHelper.setScaleX(right, mScale);
                 ViewHelper.setScaleY(right, mScale);
             }
         }
    }

    private void animateFade(View left, View right, float positionOffset) {
        if(left != null){
            ViewHelper.setAlpha(left, 1 - positionOffset);
        }
        if(right != null){
            ViewHelper.setAlpha(right, positionOffset);
        }
    }

    private void animateOutline(View left, View right) {
        if(!(left instanceof OutlineContainer))
            return;
        if(mState != State.IDLE){
            if(left != null){
                ((OutlineContainer)left).setOutlineAlpha(1.0f);
            }
            if(right != null){
                ((OutlineContainer)right).setOutlineAlpha(1.0f);
            }
        }else{
            if(left != null)
                ((OutlineContainer)left).start();
            if(right != null)
                ((OutlineContainer)right).start();
        }
    }

    @Override
    protected void onPageScrolled(int position, float offset, int offsetPixels) {
        if(mState == State.IDLE && offset > 0){
            oldPage = getCurrentItem();
            mState = position == oldPage ? State.GOING_RIGHT : State.GOING_LEFT;
        }
        boolean goingRight = position == oldPage;
        if(mState == State.GOING_RIGHT && !goingRight)
            mState = State.GOING_LEFT;
        else if(mState == State.GOING_LEFT && goingRight)
            mState = State.GOING_RIGHT;

        float effectOffset = isSmall(offset) ? 0 : offset;

        mLeft = findViewFromObject(position);
        mRight = findViewFromObject(position + 1);

        if(mFadeEnabled)
            animateFade(mLeft, mRight, effectOffset);
        if(mOutlineEnabled)
            animateOutline(mLeft, mRight);

        switch (mEffect){
            case Standard:
                break;
            case Tablet:
                animateTablet(mLeft, mRight, effectOffset);
                break;
            case CubeIn:
                animateCube(mLeft, mRight, effectOffset, true);
                break;
            case CubeOut:
                animateCube(mLeft, mRight, effectOffset, false);
                break;
            case FlipVertical:
                animateFlipVertical(mLeft, mRight, offset, offsetPixels);
                break;
            case FlipHorizontal:
                animateFlipHorizontal(mLeft, mRight, offset, offsetPixels);
                break;
            case Stack:
                animateStack(mLeft, mRight, offset, offsetPixels);
                break;
            case ZoomIn:
                animateZoom(mLeft, mRight, offset, true);
                break;
            case ZoomOut:
                animateZoom(mLeft, mRight, offset, false);
                break;
            case RotateUp:
                animateRotate(mLeft, mRight, offset, true);
                break;
            case RotateDown:
                animateRotate(mLeft, mRight, offset, false);
                break;
            case Accordion:
                animateAccordion(mLeft, mRight, offset);
                break;
        }

        super.onPageScrolled(position, offset, offsetPixels);

        if(effectOffset == 0){
            mState = State.IDLE;
        }
    }

    private void animateStack(View left, View right, float offset, int positionOffsetPixels) {
        if(mState != State.IDLE){
            if(right != null){
                mScale = (1 - SCALE_MAX) * offset + SCALE_MAX;
                mTrans = -getWidth() - getPageMargin() + positionOffsetPixels;
                ViewHelper.setScaleX(right, mScale);
                ViewHelper.setScaleY(right, mScale);
                ViewHelper.setTranslationX(right, mTrans);
            }
            if(left != null){
                 left.bringToFront();
            }
        }
    }

    private Matrix mMatrix = new Matrix();
    private Camera mCamera = new Camera();
    private float[] mTempFloat2 = new float[2];

    private float getOffsetXForRotation(float degrees, int width, int height) {
        mMatrix.reset();
        mCamera.save();
        mCamera.rotateY(Math.abs(degrees));
        mCamera.getMatrix(mMatrix);
        mCamera.restore();

        mMatrix.preTranslate(-width * 0.5f, -height * 0.5f);
        mMatrix.preTranslate(width * 0.5f, height * 0.5f);
        mTempFloat2[0] = width;
        mTempFloat2[1] = height;
        mMatrix.mapPoints(mTempFloat2);
        return (width - mTempFloat2[0]) * (degrees > 0.0f ? 1.0f : -1.0f);
    }

    public View findViewFromObject(int position) {
        Object o = mObjs.get(Integer.valueOf(position));
        if( o == null){
            return null;
        }
        PagerAdapter a= getAdapter();
        View v ;
        for(int i = 0; i < getChildCount(); i++){
             v = getChildAt(i);
            if(a.isViewFromObject(v, o))
                return v;
        }
        return null;
    }

    public void setObjectForPosition(Object obj, int position) {
         mObjs.put(Integer.valueOf(position), obj);
    }

    private boolean isSmall(float positionOffset) {
        return Math.abs(positionOffset) < 0.0001;
    }
}
